package com.rs.recruit.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * ## 自定义BaseServlet
	- XML 解析错误  语法错误   格式不佳  
	- 响应各种数据类型 send方法
	- 跨域访问 	
	- 错误界面 404
 * company 源辰信息
 * @author Administrator
 * @date 2099年12月31日
 * @version 1.0
 * Email 1198865589@qq.com
 */
public class BaseServlet extends HttpServlet{

	private static final long serialVersionUID = 1L;
	
	// TODO 课后思考
	// 8、方法太多会显示太臃肿 笨重  不友好 通我们需要考虑程序类似Service效果  重写service自动分发
	// 利用 反射 自动调用对应的业务方法自定义BaseServlet封装 service方法
	// 提示: 1、如何获取类中的方法名   2、如何确定前台发送过来的指令反射+截获url访问地址
	// /admin/* -> localhost: 8080/love/ admin/login / admin/reg

	//  重写 service 方法 实现 自动分发 调用 对应业务请求方法
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		// http://localhost:8080/Goods/show.html
		// http://localhost:8080/Goods/goods?op=finds
		
		 // 获取请求的资源地址 Goods/goods3/finds ->  Goods/goods3?op=finds
		String uri = req.getRequestURI();
		 // finds 截取最后的路径  即处理这个请求的指令值   以此对应的调用的业务方法名称 
		String path = uri.substring( uri.lastIndexOf("/") + 1);
		System.out.println( uri + "--" + path);  // finds -> add delete 
		
		// 获取当前Servlet中所有的方法 
		Method[] methods = this.getClass().getDeclaredMethods();
		// 根据path找到对应的方法名 业务 然后激活该方法
		for (Method method : methods) {
			if (Objects.equals(path, method.getName())) { // 指令名称 和 方法名 一致的情况下
				try {
					method.invoke(this, req, resp);  // 反向激活方法 
				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
					e.printStackTrace();
				}
				return; // 千万别漏掉 否则会自动执行容错界面  类似 404 ... 
			}
		}
		// 容错界面
		this.error(req, resp);
	}

	// 容错界面
	protected  void error(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		// 界面跳转 前端乱码
		resp.setContentType("text/html;charset=utf8");
		PrintWriter out = resp.getWriter();
		// http://localhost:8080/Goods/  -> http://localhost:8080/Goods/index.html
		String bathPath = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath() + "/";
		out.println("<script>alert('请求不支持!');location.href='"+bathPath+"index.html';</script>");
	}
	
	// TODO  如何封装 优化 以下问题   自定义BaseServlet的 send 来 适应 响应不同类型的数据
	/**
	 * 响应一个Object类型的参数
	 * @param response
	 * @param obj
	 * @throws IOException
	 */
	protected  void send(HttpServletResponse response, Object obj) throws IOException {
		// XML 解析错误：语法错误    格式不佳    告诉浏览器以json格式解析数据
	    response.setContentType("application/json;charset=utf-8");
	    response.setHeader("Content-Type", "application/json;charset=utf-8");
	    // 解决跨域问题 :  Access-Control-Allow-Origin 
	    response.setHeader("Access-Control-Allow-Origin", "*");
		// 响应对象 Response获取输出流 相对于JSP的九大内置对象之一 out
	    PrintWriter out = response.getWriter();
		// 利用GSON构建JSON数据
		// Gson gson = new Gson(); // 属性值为null 无法显示  导致前台显示 undefined 
		// 当配置GsonBuilder().serializeNulls().create()之后  属性值为null 也能传递到前台 方便我们的工作调试
		Gson gson = new GsonBuilder().serializeNulls().create();
		out.println( gson.toJson(obj));
	    out.flush();
	    out.close();
	}

	// TODO  根据需求进行扩展   重载send方法
	
	/**
	 * 响应一个整型类型的状态码
	 * @param response
	 * @param int
	 * @throws IOException
	 */
	protected  void send(HttpServletResponse response, int code) throws IOException {
		// 解决跨域问题 :  Access-Control-Allow-Origin 
		response.setHeader("Access-Control-Allow-Origin", "*");
	    // XML 解析错误：语法错误    格式不佳    告诉浏览器以普通文本 返回 
	    response.setContentType("text/plain;charset=utf-8");
	    PrintWriter out = response.getWriter();
	    out.println( code );
	    out.flush();
	    out.close();
	}
		
	/**
	 * 响应一个字符串类型的参数
	 * @param response
	 * @param String 
	 * @throws IOException
	*/
	protected  void send(HttpServletResponse response, String code) throws IOException {
		// XML 解析错误：语法错误    格式不佳    告诉浏览器以普通文本 返回 
	    response.setContentType("text/plain;charset=utf-8");
	    PrintWriter out = response.getWriter();
	    out.println( code );
	    out.flush();
	    out.close();
	}

	/**
	 * 以JSON响应  状态码  和 详细数据 
	 * @param response
	 * @param code 状态码
	 * @param obj 对象数据
	 * @throws IOException
	 */
	protected  void send(HttpServletResponse response, int code,  Object obj) throws IOException {
		// XML 解析错误：语法错误    格式不佳    告诉浏览器以普通文本 返回 
	    response.setContentType("application/json;charset=utf-8");
	    // 解决跨域问题 :  Access-Control-Allow-Origin 
	    response.setHeader("Access-Control-Allow-Origin", "*");
	    PrintWriter out = response.getWriter();
		// 当配置GsonBuilder().serializeNulls().create()之后  属性值为null 也能传递到前台 方便我们的工作调试
		Gson gson = new GsonBuilder().serializeNulls().create();
		// 存储空键或值 都不允许 空值会导致空指针易异常 NullPointerException if any key or value is {@code null}
		// Map<String, Object> map = Map.of("code", code, "data", obj);
		Map<String, Object> map  = new HashMap<String, Object>();
		map.put("code", code);
		map.put("data", obj);
		out.println( gson.toJson(map));
	    out.flush();
	    out.close();
	}

	// TODO  利用重载的概念 自定义数据格式   课后扩展: 专属的响应枚举对象类设计  obj  msg code   
}
